/*
    异步日志器的工作器模块实现
*/

#ifndef __M_LOPPER_H__
#define __M_LOPPER_H__

#include <iostream>
#include <mutex>
#include <atomic>
#include <thread>
#include <condition_variable>
#include <functional>
#include <memory>
#include "buffer.hpp"
 
namespace mylog{
    enum class LooperType{
        ASYNC_UNSAFE,   // 不安全类型 对应缓冲区空间不足时进行扩容 用于极限压力测试 实际中不使用
        ASYNC_ASFE      // 安全类型   对应缓冲区空间不足时阻塞等待
    };

    class AsyncLooper
    {
    public:
        using func_t = std::function<void(Buffer &)>;
        using ptr = std::shared_ptr<AsyncLooper>;

    public:
        AsyncLooper(const func_t callback, const LooperType lopper_type)
        :_stop(false),
        _callback(callback),
        _thread(std::thread(&AsyncLooper::threadentry, this)),
        _lopper_type(lopper_type)
        {}

        ~AsyncLooper()
        {
            stop();
        }

        void stop(){
            // 工作器工作状态设置为停止 并唤醒所有异步工作线程
            _stop = true;
            _cond_pro.notify_all();
            _cond_con.notify_all();
            if(_thread.joinable()){
                _thread.join();
            }
        }

        void push(const char *data, size_t len){
            // 1.利用unqiue_lock自动管理加锁和释放锁
            std::unique_lock<std::mutex> lock(_mutex);
            if (_lopper_type == LooperType::ASYNC_ASFE){
                // 安全类型下 生产者条件变量判断 生产缓冲区的可写数据大小是否大于要写入的数据大小
                // 这里存在一个特殊情况 如果单挑日志大小就大于缓冲区大小 那业务线程就会一直阻塞在这里
                _cond_pro.wait(lock, [&]()
                               { return _stop || len <= _pro_buffer.WriteSize(); });
            }
            // 2.满足就向生产缓冲区中写入数据
            _pro_buffer.push(data, len);
            // 3.唤醒一个异步工作线程进行处理
            _cond_con.notify_one();
        }

    private:
        // 异步工作线程入口函数
        void threadentry(){
            // 错误点四:异步线程没有循环执行 而是执行一次就结束 导致业务线程写满缓冲区后一直阻塞等待
            while(1){
                // 利用作用域管理锁的生命周期
                {
                    // 1.利用unqiue_lock自动管理加锁和释放锁
                    std::unique_lock<std::mutex> lock(_mutex);
                    // 2.消费者条件变量判断 a.生产缓冲区不为空 可以交换为消费缓冲区 b.工作器停止
                    _cond_con.wait(lock, [&]()
                                   { return _stop || !_pro_buffer.Empty(); });
                    if(_stop && _pro_buffer.Empty()){
                        break;
                    }
                    // 3.条件满足 交换两个缓冲区
                    _con_buffer.SwapBuffer(_pro_buffer);
                    if (_lopper_type == LooperType::ASYNC_ASFE)
                    {
                        // 安全类型下 唤醒一个业务线程继续向生产缓冲区写入数据
                        _cond_pro.notify_one();
                    }
                }
                // 4.异步工作线程调用回调函数 处理消费缓冲区中的数据 然后置空
                _callback(_con_buffer);
                _con_buffer.ReSet();
            }
        }

    private:
        func_t _callback;                     // 工作线程的回调函数

    private:
        LooperType _lopper_type;              // 工作器类型 安全/非安全类型
        std::atomic<bool> _stop;              // 工作器运行状态标志 设置为原子类型
        Buffer _pro_buffer;                   // 生产缓冲区
        Buffer _con_buffer;                   // 消费缓冲区
        std::mutex _mutex;                    // 互斥锁
        std::condition_variable _cond_pro;    // 生产者条件变量
        std::condition_variable _cond_con;    // 消费者条件变量
        std::thread _thread;                  // 异步工作线程
    };
}

#endif